/*
 * Copyright (c) 2001, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.encoding;

import java.io.IOException;
import java.nio.ByteBuffer;

import org.omg.CORBA.Any;
import org.omg.CORBA.Principal;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.portable.InputStream;

import com.sun.corba.se.pept.encoding.OutputObject;
import com.sun.corba.se.pept.protocol.MessageMediator;

import com.sun.corba.se.spi.encoding.CorbaOutputObject;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.protocol.CorbaMessageMediator;
import com.sun.corba.se.pept.transport.ByteBufferPool;
import com.sun.corba.se.spi.transport.CorbaConnection;
import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.impl.encoding.BufferManagerFactory;
import com.sun.corba.se.impl.encoding.ByteBufferWithInfo;
import com.sun.corba.se.impl.encoding.CDROutputStream;
import com.sun.corba.se.impl.encoding.CDROutputStream_1_0;
import com.sun.corba.se.impl.encoding.CodeSetConversion;
import com.sun.corba.se.impl.encoding.CodeSetComponentInfo;
import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.protocol.giopmsgheaders.Message;
import com.sun.corba.se.impl.protocol.giopmsgheaders.MessageBase;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.logging.OMGSystemException;

/**
 * @author Harold Carr
 */
public class CDROutputObject extends CorbaOutputObject {

  private Message header;
  private ORB orb;
  private ORBUtilSystemException wrapper;
  private OMGSystemException omgWrapper;

  // REVISIT - only used on sendCancelRequest.
  private CorbaConnection connection;

  private CDROutputObject(
      ORB orb, GIOPVersion giopVersion, Message header,
      BufferManagerWrite manager, byte streamFormatVersion,
      CorbaMessageMediator mediator) {
    super(orb, giopVersion, header.getEncodingVersion(),
        false, manager, streamFormatVersion,
        ((mediator != null && mediator.getConnection() != null) ?
            ((CorbaConnection) mediator.getConnection()).
                shouldUseDirectByteBuffers() : false));

    this.header = header;
    this.orb = orb;
    this.wrapper = ORBUtilSystemException.get(orb, CORBALogDomains.RPC_ENCODING);
    this.omgWrapper = OMGSystemException.get(orb, CORBALogDomains.RPC_ENCODING);

    getBufferManager().setOutputObject(this);
    this.corbaMessageMediator = mediator;
  }

  public CDROutputObject(ORB orb,
      MessageMediator messageMediator,
      Message header,
      byte streamFormatVersion) {
    this(
        orb,
        ((CorbaMessageMediator) messageMediator).getGIOPVersion(),
        header,
        BufferManagerFactory.newBufferManagerWrite(
            ((CorbaMessageMediator) messageMediator).getGIOPVersion(),
            header.getEncodingVersion(),
            orb),
        streamFormatVersion,
        (CorbaMessageMediator) messageMediator);
  }

  // NOTE:
  // Used in SharedCDR (i.e., must be grow).
  // Used in msgtypes test.
  public CDROutputObject(ORB orb,
      MessageMediator messageMediator,
      Message header,
      byte streamFormatVersion,
      int strategy) {
    this(
        orb,
        ((CorbaMessageMediator) messageMediator).getGIOPVersion(),
        header,
        BufferManagerFactory.
            newBufferManagerWrite(strategy,
                header.getEncodingVersion(),
                orb),
        streamFormatVersion,
        (CorbaMessageMediator) messageMediator);
  }

  // REVISIT
  // Used on sendCancelRequest.
  // Used for needs addressing mode.
  public CDROutputObject(ORB orb, CorbaMessageMediator mediator,
      GIOPVersion giopVersion,
      CorbaConnection connection, Message header,
      byte streamFormatVersion) {
    this(
        orb,
        giopVersion,
        header,
        BufferManagerFactory.
            newBufferManagerWrite(giopVersion,
                header.getEncodingVersion(),
                orb),
        streamFormatVersion,
        mediator);
    this.connection = connection;
  }

  // XREVISIT
  // Header should only be in message mediator.
  // Another possibility: merge header and message mediator.
  // REVISIT - make protected once all encoding together
  public Message getMessageHeader() {
    return header;
  }

  public final void finishSendingMessage() {
    getBufferManager().sendMessage();
  }

  /**
   * Write the contents of the CDROutputStream to the specified
   * output stream.  Has the side-effect of pushing any current
   * Message onto the Message list.
   *
   * @param s The output stream to write to.
   */
  public void writeTo(CorbaConnection connection)
      throws java.io.IOException {

    //
    // Update the GIOP MessageHeader size field.
    //

    ByteBufferWithInfo bbwi = getByteBufferWithInfo();

    getMessageHeader().setSize(bbwi.byteBuffer, bbwi.getSize());

    if (orb() != null) {
      if (((ORB) orb()).transportDebugFlag) {
        dprint(".writeTo: " + connection);
      }
      if (((ORB) orb()).giopDebugFlag) {
        CDROutputStream_1_0.printBuffer(bbwi);
      }
    }
    bbwi.byteBuffer.position(0).limit(bbwi.getSize());
    connection.write(bbwi.byteBuffer);
  }

  /**
   * overrides create_input_stream from CDROutputStream
   */
  public org.omg.CORBA.portable.InputStream create_input_stream() {
    // XREVISIT
    return null;
    //return new XIIOPInputStream(orb(), getByteBuffer(), getIndex(),
    //isLittleEndian(), getMessageHeader(), conn);
  }

  public CorbaConnection getConnection() {
    // REVISIT - only set when doing sendCancelRequest.
    if (connection != null) {
      return connection;
    }
    return (CorbaConnection) corbaMessageMediator.getConnection();
  }

  // XREVISIT - If CDROutputObject doesn't live in the iiop
  // package, it will need this, here, to give package access
  // to xgiop.
  // REVISIT - make protected once all encoding together
  public final ByteBufferWithInfo getByteBufferWithInfo() {
    return super.getByteBufferWithInfo();
  }

  // REVISIT - make protected once all encoding together
  public final void setByteBufferWithInfo(ByteBufferWithInfo bbwi) {
    super.setByteBufferWithInfo(bbwi);
  }

  /**
   * Override the default CDR factory behavior to get the
   * negotiated code sets from the connection.
   *
   * These are only called once per message, the first time needed.
   *
   * In the local case, there is no Connection, so use the
   * local code sets.
   */
  protected CodeSetConversion.CTBConverter createCharCTBConverter() {
    CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

    // If the connection doesn't have its negotiated
    // code sets by now, fall back on the defaults defined
    // in CDRInputStream.
    if (codesets == null) {
      return super.createCharCTBConverter();
    }

    OSFCodeSetRegistry.Entry charSet
        = OSFCodeSetRegistry.lookupEntry(codesets.getCharCodeSet());

    if (charSet == null) {
      throw wrapper.unknownCodeset(charSet);
    }

    return CodeSetConversion.impl().getCTBConverter(charSet,
        isLittleEndian(),
        false);
  }

  protected CodeSetConversion.CTBConverter createWCharCTBConverter() {

    CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

    // If the connection doesn't have its negotiated
    // code sets by now, we have to throw an exception.
    // See CORBA formal 00-11-03 13.9.2.6.
    if (codesets == null) {
      if (getConnection().isServer()) {
        throw omgWrapper.noClientWcharCodesetCtx();
      } else {
        throw omgWrapper.noServerWcharCodesetCmp();
      }
    }

    OSFCodeSetRegistry.Entry wcharSet
        = OSFCodeSetRegistry.lookupEntry(codesets.getWCharCodeSet());

    if (wcharSet == null) {
      throw wrapper.unknownCodeset(wcharSet);
    }

    boolean useByteOrderMarkers
        = ((ORB) orb()).getORBData().useByteOrderMarkers();

    // With UTF-16:
    //
    // For GIOP 1.2, we can put byte order markers if we want to, and
    // use the default of big endian otherwise.  (See issue 3405b)
    //
    // For GIOP 1.1, we don't use BOMs and use the endianness of
    // the stream.
    if (wcharSet == OSFCodeSetRegistry.UTF_16) {
      if (getGIOPVersion().equals(GIOPVersion.V1_2)) {
        return CodeSetConversion.impl().getCTBConverter(wcharSet,
            false,
            useByteOrderMarkers);
      }

      if (getGIOPVersion().equals(GIOPVersion.V1_1)) {
        return CodeSetConversion.impl().getCTBConverter(wcharSet,
            isLittleEndian(),
            false);
      }
    }

    // In the normal case, let the converter system handle it
    return CodeSetConversion.impl().getCTBConverter(wcharSet,
        isLittleEndian(),
        useByteOrderMarkers);
  }

  // If we're local and don't have a Connection, use the
  // local code sets, otherwise get them from the connection.
  // If the connection doesn't have negotiated code sets
  // yet, then we use ISO8859-1 for char/string and wchar/wstring
  // are illegal.
  private CodeSetComponentInfo.CodeSetContext getCodeSets() {
    if (getConnection() == null) {
      return CodeSetComponentInfo.LOCAL_CODE_SETS;
    } else {
      return getConnection().getCodeSetContext();
    }
  }

  protected void dprint(String msg) {
    ORBUtility.dprint("CDROutputObject", msg);
  }
}

// End of file.
